﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Web;
using NRails.Database;
using NRails.Database.Schema;
using NRails.Database.Mssql;
using BLToolkit.Data;
using log4net;
using Nemerle.Logging;
using NRails.Spark;

[assembly: LogCondition(Engine.Logger != null)]
[assembly: LogFunction (Debug => Engine.Logger.Debug, 
                        Info => Engine.Logger.Info, 
                        Warn => Engine.Logger.Warn, 
                        Error => Engine.Logger.Error, 
                        Fatal => Engine.Logger.Fatal)]
[assembly: LogFlag(Debug, true)]
[assembly: LogFlag(Info, true)]

namespace NRails
{
  public class Engine
  {
      [Accessor]
      static logger : ILog = LogManager.GetLogger("NRails");

      [Accessor]
      static mutable siteAssembly : string;

      [Accessor]
      cfg : Configuration;
      
      public this(cfg : Configuration)
      {
          this.cfg = cfg;
      }
      
      public static Init() : void
      {
        siteAssembly = Assembly.GetCallingAssembly().FullName;
        SparkNemerleEngineStarter.RegisterViewEngine();
      }
      
      public GetSchema() : DBSchema
      {
          GetSchema(cfg.Env);
      }
      
      public CreateDbManager() : DbManager
      {
          DbManager(cfg.ConnectionStringName);
      }
      
      public CreateDbDriver() : IDBDriver
      {
          CreateDbDriver(cfg.Env);
      }
      
      public CreateDbDriver(env : string) : IDBDriver
      {
          def connStr = cfg.GetConnectionString(env);
          match (connStr.ProviderName)
          {
              | "System.Data.SqlClient" => MssqlDriver();
              | x => throw NotImplementedException($"Driver for '$x' provider is not implemented");
          }
      }
      
      mutable cachedSchema : DBSchema;
      public GetSchema(env : string) : DBSchema
      {
          when (cachedSchema == null)
          {
            def connStr = cfg.GetConnectionString(env).ConnectionString;
            def db = CreateDbDriver(env);
            def schemaDriver = db.CreateSchemaDriver();
            cachedSchema = schemaDriver.LoadExistingSchema(connStr);
          }

          cachedSchema;
      }

      public GetTable(typeName : string) : TableSchema
      {
          def sch = GetSchema();
          
          sch.Tables.FirstOrDefault(t => t.Name == typeName);
      }
      
      mutable static instance : Engine;
      
      public static Instance : Engine 
      { 
        get 
        { 
          when (instance == null)
          {
            instance = Engine(ConfigReader(RunTimeConfigLocator()).Read())
          }
          instance;
        }
      }
  }
}
